#include "DGL3ProgramMathDefs.inc"
#include "DGL3ProgramEnums.inc"
#include "DGL3Common.glsl"
#include "DGL3LightsUniform.glsl"
#include "DGL3ClipUniform.glsl"

//-----------------------------------------

uniform MaterialData
{
	MaterialSource uMaterial;
};

uniform	vec4 uMarkColor;

uniform	bool uUseDiffTex;
uniform	int uUseBumpTex;
uniform	float uLodEnv;

uniform	int uMappingType;
uniform	vec2 uMappingParams;
uniform	mat4 uMappingMatrix;
uniform	mat3 uTextureMatrix;

uniform	vec3 uThreadPos0;
uniform	vec3 uThreadDir0;
uniform	float uThreadTurns0;
uniform	vec3 uThreadPos1;
uniform	vec3 uThreadDir1;
uniform	float uThreadTurns1;
uniform	int uThreadNum;

uniform	vec3 uZebraFirstColor;
uniform	int uZebraType;
uniform	vec3 uZebraSecondColor;
uniform	float uZebraPrjOffset;

//-----------------------------------------

#ifdef VERTEX_SHADER

	#include "DGL3Clip.glsl"
	
	layout(location = GE_VA_Position) in vec3 aPosition;
	layout(location = GE_VA_Normal) in vec3 aNormal;
	layout(location = GE_VA_Texture) in vec2 aUV;
	layout(location = GE_VA_Transform) in ivec2 aAssigns;
	layout(location = GE_VA_FaceIndex) in int aFaceIndex;

	out vec3 vNormal;
	out vec3 vWorldPosition;
	out vec3 vTexCoord;
	out vec3 vPos;
	out vec3 vTangent;
	flat out ivec2 vAssigns;
	flat out int vFaceIndex;
	
	vec3 GetAutomaticTangent(vec3 nNormal)
	{
		vec3 nAbsnormal2 = abs(nNormal);
	
		float cmp1Val = max(nAbsnormal2.x, max(nAbsnormal2.y, nAbsnormal2.z));
		vec3 cmp1 = vec3(0.0, 0.0, 1.0);
		if (nAbsnormal2.x == cmp1Val)
			cmp1 = vec3(1.0, 0.0, 0.0);
		else if (nAbsnormal2.y == cmp1Val)
			cmp1 = vec3(0.0, 1.0, 0.0);
	
		float nSign1 = dot(sign(nNormal), cmp1);
		if (cmp1.x != 0.0)
			return vec3(0.0, -nSign1, 0.0);
		else if (cmp1.y != 0.0)
			return vec3(nSign1, 0.0, 0.0);
			
		return vec3(nSign1, 0.0, 0.0);
	}
	
	void main(void)
	{
		vec4 worldPosition = GetModelMatrix(aAssigns.x) * vec4(aPosition, 1.0);
		vPos = aPosition;
		vNormal = aNormal;
		vAssigns = ivec2(aAssigns.x, 0);
		vFaceIndex = aFaceIndex;

		vec4 viewPosition = uViewMatrix * worldPosition;
		gl_Position = uProjectionMatrix * viewPosition;
		vWorldPosition = worldPosition.xyz;

		if (uMappingType == GE_MAPPING_UV)
		{
			vTexCoord = vec3(aUV, 0.0);
			vTangent = GetAutomaticTangent(aNormal);
		}
		else
		{
			vTexCoord = vec3(uMappingMatrix * vec4(aPosition, 1.0));
			vTangent = GetAutomaticTangent(aNormal);
		}
		
		if (uNumClipPlanes > 0)
			SetClipDistance(0, dot(worldPosition, uClipPlane_0));			 
		if (uNumClipPlanes > 1)
			SetClipDistance(1, dot(worldPosition, uClipPlane_1));			 
		if (uNumClipPlanes > 2)
			SetClipDistance(2, dot(worldPosition, uClipPlane_2));			 
		if (uNumClipPlanes > 3)
			SetClipDistance(3, dot(worldPosition, uClipPlane_3));			 
		if (uNumClipPlanes > 4)
			SetClipDistance(4, dot(worldPosition, uClipPlane_4));			 
		if (uNumClipPlanes > 5)
			SetClipDistance(5, dot(worldPosition, uClipPlane_5));		 
	}

#endif

//-----------------------------------------

#ifdef DEBUG_NORMALS_GSMain

layout(triangles) in;
layout(line_strip, max_vertices = 6) out;

flat in ivec2 vAssigns[];
in vec3 vNormal[];
in vec3 vPos[];
in vec3 vTangent[];

out vec4 vColor;

void main(void)
{
	mat3 normalMatrix = GetNormalMatrix(vAssigns[0].x);
	mat3 TBNMatrix = GetMatrixTBN(normalMatrix, vNormal[0], vTangent[0], false);

	mat4 matrix = uProjectionMatrix * uViewMatrix * GetModelMatrix(vAssigns[0].x);
	
	gl_Position = matrix * vec4(vPos[0], 1.0);
	vColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
	EmitVertex();
	gl_Position = matrix * vec4(vPos[0] + TBNMatrix[2]/100.0f, 1.0);
	EmitVertex();
	EndPrimitive();
	
	gl_Position = matrix * vec4(vPos[0], 1.0);
	vColor = vec4(0.0f, 0.0f, 1.0f, 1.0f);
	EmitVertex();
	gl_Position = matrix * vec4(vPos[0] + TBNMatrix[0]/50.0f, 1.0);
	EmitVertex();
	EndPrimitive();
	
	gl_Position = matrix * vec4(vPos[0], 1.0);
	vColor = vec4(0.0f, 1.0f, 0.0f, 1.0f);
	EmitVertex();
	gl_Position = matrix * vec4(vPos[0] + TBNMatrix[1]/50.0f, 1.0);
	EmitVertex();
	EndPrimitive();
}

#endif

//-----------------------------------------

#ifdef DEBUG_NORMALS_FSMain

in vec4 vColor;

void main(void)
{
	gl_FragColor = vColor;
}

#endif

//-----------------------------------------

#ifdef FRAGMENT_SHADER

	uniform sampler2D uEnvTex;
	uniform sampler2D uDiffTex;
	uniform sampler2D uBumpTex;
	uniform int uFaceIdOffset;
	
	in vec3 vNormal;
	in vec3 vWorldPosition;
	in vec3 vTexCoord;
	in vec3 vPos;
	in vec3 vTangent;
	flat in ivec2 vAssigns;
	flat in int vFaceIndex;

	layout(location = 0) out vec4 fragColor;
#ifdef OIT
	layout(location = 1) out vec4 fragCount;
#endif

	layout(location = 2) out int faceIndexOut;
	layout(location = 3) out float externalDepth;

	vec3 GetIntensiveCoeff(float intensity)
	{
		if (intensity > 0.95)	   return vec3(1.0);
		else if (intensity > 0.75) return vec3(0.8);
		else if (intensity > 0.50) return vec3(0.6);
		else if (intensity > 0.25) return vec3(0.4);
		
		return vec3(0.2);
	}

	vec2 GetAutomaticTexCoords(vec3 nPos, vec3 nNormal, mat3 n2xMatrix)
	{
		vec3 absNormal = abs(nNormal);
		float maxAxis = max(absNormal.x, max(absNormal.y, absNormal.z));

		vec3 mapCoord;
		if (absNormal.x == maxAxis)
		{
			mapCoord = vec3(nPos.y * sign(nNormal.x), nPos.z, 1.0);
		}
		else if (absNormal.y == maxAxis)
		{			
			mapCoord = vec3(nPos.x * -sign(nNormal.y), nPos.z, 1.0);
		}
		else 
		{
			mapCoord = vec3(-nPos.x, nPos.y * -sign(nNormal.z), 1.0);
		}
	
		return (n2xMatrix * mapCoord).xy;
	}
	
	vec2 GetBoxTexCoords(vec3 nPos, vec3 nNormal, mat3 n2xMatrix)
	{
		vec3 mapNormal = FormNormalMatrix(uMappingMatrix, false) * nNormal;
		return GetAutomaticTexCoords(nPos, mapNormal, n2xMatrix);
	}
	
	vec2 GetCylindricalTexCoords(vec3 nPos, mat3 n2xMatrix)
	{
		vec2 nTexcoord = vec2((atan(nPos.y, nPos.x) + GE_M_PI) / uMappingParams[1], nPos.z);
		nTexcoord = (n2xMatrix * vec3(nTexcoord, 1.0f)).xy;
		return nTexcoord;
	}
	
	vec2 GetSphericalTexCoords(vec3 nPos, mat3 n2xMatrix)
	{
		vec2 nTexcoord = vec2((atan(nPos.y, nPos.x) + GE_M_PI), 
							  (atan(nPos.z, length(nPos.xy)) + GE_M_PI / 2.0)) / uMappingParams;
		nTexcoord = (n2xMatrix * vec3(nTexcoord, 1.0f)).xy;
		return nTexcoord;
	}

	vec2 GetPlaneTexCoords(vec3 nPos, mat3 n2xMatrix)
	{
		vec2 nTexcoord = nPos.xy;
		
		nTexcoord = (n2xMatrix * vec3(nTexcoord, 1.0f)).xy;
		return nTexcoord;
	}
	
	vec2 GetSphereTexCoord(vec3 dir)
	{
		float theta = atan(dir.x, dir.y);
		float phi = GE_M_PI * 0.5f - acos(dir.z);
		float u = (theta + GE_M_PI) * (0.5f * GE_M_1_PI);
		float v = 0.5f * (1.0f + sin(phi));
		return vec2(1.0f - u, v);
	}
	
	vec2 GetEnvSphereTexCoords(vec3 nPos, vec3 nNormal)
	{
		mat4 modelViewMatrix = uViewMatrix * GetModelMatrix(vAssigns.x);
		vec4 u = normalize(modelViewMatrix * vec4(nPos, 1.0));
		vec3 n = FormNormalMatrix(modelViewMatrix, false) * nNormal;
		vec3 r = reflect(u.xyz, n);
		float m = 2.0 * sqrt(r.x*r.x + r.y*r.y + (r.z + 1.0)*(r.z + 1.0));
		return vec2(r.x / m, r.y / m) + vec2(0.5);
	}
	
	vec2 GetZebraTexCoords(vec3 nPos, vec3 nNormal)
	{
		mat4 modelViewMatrix = uViewMatrix * GetModelMatrix(vAssigns.x);
		vec4 mvPos = modelViewMatrix * vec4(nPos, 1.0);
		vec3 dir = normalize(vec3(0.0, 0.0, mvPos.z - uZebraPrjOffset) - mvPos.xyz);
		vec3 n = FormNormalMatrix(modelViewMatrix, false) * nNormal;
		vec3 r = reflect(dir, n);
		float m = 2.0 * sqrt(r.x*r.x + r.y*r.y + (r.z + 1.0)*(r.z + 1.0));
		return vec2(r.x / m, r.y / m) + vec2(0.5);
	}
	
	vec2 SphereMapCoord(vec3 envcoord)
	{
		vec3 d = normalize(envcoord);
		return vec2(atan(d.z, d.x)*GE_M_1_PI*0.5 - 0.25, asin(d.y) * GE_M_1_PI + 0.5);
	}

	vec2 GetTexCoord(vec3 textCoord, vec3 normal, mat3 n2xMatrix)
	{
		switch (uMappingType)
		{
		case GE_MAPPING_Auto:
			return GetAutomaticTexCoords(textCoord, normal, n2xMatrix);
		case GE_MAPPING_Box:
			return GetBoxTexCoords(textCoord, normal, n2xMatrix);
		case GE_MAPPING_UV:
			return (n2xMatrix * textCoord).xy * uMappingParams;
		case GE_MAPPING_Sphere:
			return GetSphericalTexCoords(textCoord, n2xMatrix);
		case GE_MAPPING_Cylinder:
			return GetCylindricalTexCoords(textCoord, n2xMatrix);
		case GE_MAPPING_Plane:
			return GetPlaneTexCoords(textCoord, n2xMatrix);
		}
		
		// Two switches is not accidental, otherwise the driver of AMD will crash.
		
		switch (uMappingType)
		{
		case GE_MAPPING_SphereEnv:
			return GetEnvSphereTexCoords(textCoord, normal);
		case GE_MAPPING_ZebraEnv:
			return GetZebraTexCoords(textCoord, normal);
		}
		
		return textCoord.xy;
	}
	
	//---------------------------------
	
	vec3 ThreadProjection(vec3 a, vec3 b)
	{
	   vec3 na = normalize(a);
	   return na * dot(na, b);
	}
	
	float Thread_y(float fi, float H, float L)
	{
	   return mod(H - (fi/(2.0 * GE_M_PI)) * L, L);
	}
	
	float GetThreadRadius(float fi, float H, float L)
	{
	   float y = Thread_y(fi, H, L);
	   if (0.0 <= y && y <= L/2.0)
		  y = y/tan(/*alf*/1.0f);
	   else if (L/2.0 < y && y < L)
			y = (L - y)/tan(/*alf*/1.0f);
	   return y;
	}
	
	vec3 GetThreadNormal(vec3 p)
	{
	  p = abs(p);
	  float m = min(p.x, min(p.y, p.z));
	  int i = 0;
	  if (p.y == m)
		 i = 1;
	  else if (p.z == m)
		 i = 2;
		 
	  vec3 b = vec3(0.0);
	  b[i] = 1;
	  return normalize(cross(b, p));
	}
	
	float CalcThreadTerm(vec3 position, vec3 t_pos, vec3 t_dir, vec3 t_norm, float t_cnt, inout vec3 normal)
	{
	   float threadLen = length(t_dir);
	   float L = threadLen/abs(t_cnt);
	   
	   vec3 p = normalize(t_dir);
		  
	   vec3 pos = position - t_pos;
	   vec3 prj = ThreadProjection(p, pos);
	   float lenPrj = length(prj);
	   prj = normalize(prj);
	   
	   bvec3 b = lessThan(abs(prj - p), vec3(0.1f));
	   if (lenPrj <= threadLen && all(b) == true)
	   {
		  vec3 norm = normalize(cross(p, pos));
		  vec3 py = GetThreadNormal(p);
		  
		  vec3 binormal = normalize(cross(norm, p));
		  //if (binormal.z < 0)
			//binormal = -binormal;
			
		  mat3 TBN = mat3(p, norm, binormal);
		  
		  float d = dot(norm, py);
		  float fi = sign(t_cnt) * acos(d);
		  
		  vec3 dir = normalize(cross(norm, py));
		  bvec3 b = lessThan(abs(dir - p), vec3(0.1f));
		  
		  if (all(b) == true)
		  {
			 fi = 2.0f * GE_M_PI - fi;
		  }
		  
		  float sig = sign(Thread_y(fi, lenPrj, L) - L * 0.5);
		  vec3 norm_vec = vec3(-sig * 0.5, 0.0, 0.8);
		  
		  normal = normalize(GetNormalMatrix(vAssigns.x) * normalize(TBN * norm_vec));
		  
		  vec3 faceNormal = normalize(GetNormalMatrix(vAssigns.x) * normalize(TBN * vec3(0.0, 0.0, 1.0)));
		  normal = faceforward(normal, uViewDirection, faceNormal);
		  
		  float r = GetThreadRadius(fi, lenPrj, L);
		  //vec4 k = vec4(0.0f) - gl_Color;
		  //k.a = 0.0f;

		  return /*k * */ r / (L * 0.5);
	   }
	   
	  return 0.0f;
	}
	
	vec3 GetNormalFromNormalMap(sampler2D normalMap, vec2 commonCoord)
	{
		vec3 tBumpNormal = 2.0*vec3(texture2D(normalMap, commonCoord))-vec3(1.0);
		return normalize(tBumpNormal);
	}
	vec3 GetNormalFromHeightMap(sampler2D heightMap, vec2 uvOffset, float scale)
	{
		const float texelSize = 0.001;
		vec4 heights;
		heights[0] = texture2D(heightMap, uvOffset + vec2( 0,  1) * texelSize).x;
		heights[1] = texture2D(heightMap, uvOffset + vec2( 0, -1) * texelSize).x;
		heights[2] = texture2D(heightMap, uvOffset + vec2(-1,  0) * texelSize).x;
		heights[3] = texture2D(heightMap, uvOffset + vec2( 1,  0) * texelSize).x;

		vec3 normal;
		normal.x = (heights[2] - heights[3]) * scale;
		normal.y = (heights[0] - heights[1]) * scale;
		normal.z = 0.5;
		return normalize(normal);
	}
	
	vec2 ParallaxMapping(vec2 texCoords, mat3 matrixTBN, vec3 viewDirection)
	{
		vec3 viewDir = inverse(matrixTBN) * viewDirection;
		vec2 vParallaxDirection = normalize(viewDir.xy);
	
		float fParallaxLength = -sqrt( 1.0 - viewDir.z * viewDir.z ) / viewDir.z; 

		float parallaxPerspectiveBias = 0.05;
		float fParallaxBias = parallaxPerspectiveBias + (1.0 - parallaxPerspectiveBias) * ( 2.0 * viewDir.z - 1.0 );
	
		float parallaxOffset = 0.02f;
		float po = parallaxOffset;
		if (uUseBumpTex != GE_BUMP_Normal)
			po *= 2.0;
			
		if (po < 0.0)
			po *= -1.0;
		
		vec2 parallax = -vParallaxDirection * fParallaxLength * fParallaxBias * po; 
	
		float x;
		float y;
		float xh;
		float yh;
	
	
		float h0,h1,h2,h3,h4,h5,h6,h7;
			if (uUseBumpTex != GE_BUMP_Normal)
			{
				h0 = texture2D( uBumpTex, texCoords - parallax * 1.000 ).r;
				h1 = texture2D( uBumpTex, texCoords - parallax * 0.875 ).r;
				h2 = texture2D( uBumpTex, texCoords - parallax * 0.750 ).r;
				h3 = texture2D( uBumpTex, texCoords - parallax * 0.625 ).r;
				h4 = texture2D( uBumpTex, texCoords - parallax * 0.500 ).r;
				h5 = texture2D( uBumpTex, texCoords - parallax * 0.375 ).r;
				h6 = texture2D( uBumpTex, texCoords - parallax * 0.250 ).r;
				h7 = texture2D( uBumpTex, texCoords - parallax * 0.125 ).r;
			}
			else
			{
				h0 = texture2D( uBumpTex, texCoords - parallax * 1.000 ).w;
				h1 = texture2D( uBumpTex, texCoords - parallax * 0.875 ).w;
				h2 = texture2D( uBumpTex, texCoords - parallax * 0.750 ).w;
				h3 = texture2D( uBumpTex, texCoords - parallax * 0.625 ).w;
				h4 = texture2D( uBumpTex, texCoords - parallax * 0.500 ).w;
				h5 = texture2D( uBumpTex, texCoords - parallax * 0.375 ).w;
				h6 = texture2D( uBumpTex, texCoords - parallax * 0.250 ).w;
				h7 = texture2D( uBumpTex, texCoords - parallax * 0.125 ).w;
			}
			
			if(parallaxOffset < 0.0)
			{
				h0 = 1.0-h0;
				h1 = 1.0-h1;
				h2 = 1.0-h2;
				h3 = 1.0-h3;
				h4 = 1.0-h4;
				h5 = 1.0-h5;
				h6 = 1.0-h6;
				h7 = 1.0-h7;
			}

			if      (h7 > 0.875) { x = 0.937; y = 0.938; xh = h7; yh = h7; }
			else if (h6 > 0.750) { x = 0.750; y = 0.875; xh = h6; yh = h7; }
			else if (h5 > 0.625) { x = 0.625; y = 0.750; xh = h5; yh = h6; }
			else if (h4 > 0.500) { x = 0.500; y = 0.625; xh = h4; yh = h5; }
			else if (h3 > 0.375) { x = 0.375; y = 0.500; xh = h3; yh = h4; }
			else if (h2 > 0.250) { x = 0.250; y = 0.375; xh = h2; yh = h3; }
			else if (h1 > 0.125) { x = 0.125; y = 0.250; xh = h1; yh = h2; }
			else                 { x = 0.000; y = 0.125; xh = h0; yh = h1; }
			
		float fParallaxEffect = (x * (y - yh) - y * (x - xh))/((y - yh) - (x - xh));
		return texCoords - parallax * (1.0 - fParallaxEffect );
	}
	
	float mad(float a,float b,float c)
	{
		return a*b + c;
	}
	
	float fresnelTerm(vec3 light, vec3 normal, float R0)
	{
		float cosAngle = 1-clamp(dot(light, normal),0.0,1.0);
		float result = cosAngle * cosAngle;
		result = result * result;
		result = result * cosAngle;
		float _R0 = clamp(R0, 0.0, 1.0);
		result = clamp(mad(result, 1-_R0, R0), 0.0, 1.0);
		return result;
	}
	
	void main(void)
	{
		mat3 normalMatrix = GetNormalMatrix(vAssigns.x);
		vec3 normal = normalize(normalMatrix * vNormal);
		
		vec2 texCoord = GetTexCoord(vTexCoord, vNormal, uTextureMatrix);
		vec3 specularMat = uMaterial.Specular;
		
		if (uUseBumpTex != GE_BUMP_None)
		{
			mat3 TBNMatrix = GetMatrixTBN(normalMatrix, vNormal, vTangent, false);
			
			switch (uUseBumpTex)
			{
			case GE_BUMP_Parallax:
				texCoord = ParallaxMapping(texCoord, TBNMatrix, uViewDirection);
				break;
			case GE_BUMP_Normal:
				normal = normalize(TBNMatrix * GetNormalFromNormalMap(uBumpTex, texCoord));
				break;
			case GE_BUMP_Height:
				normal = normalize(TBNMatrix * GetNormalFromHeightMap(uBumpTex, texCoord, 0.5f));
				break;
			case GE_BUMP_Specular:
				specularMat = texture2D(uBumpTex, texCoord).rgb * uMaterial.Specular;
				break;
			}
		}
		
		if (!gl_FrontFacing)
			normal = -normal;
		
		#ifdef THREAD
		
		float threadTerm = 0.0;
		if (uThreadNum > 0)
		{
			vec3 thredNormal = GetThreadNormal(normalize(uThreadDir0));
			threadTerm += CalcThreadTerm(vPos, uThreadPos0, uThreadDir0, thredNormal, uThreadTurns0, normal);
			
			if (uThreadNum > 1)
			{
				thredNormal = GetThreadNormal(normalize(uThreadDir1));
				threadTerm += CalcThreadTerm(vPos, uThreadPos1, uThreadDir1, thredNormal, uThreadTurns1, normal);
			}
		}
		
		#endif
		
		vec3 diffTex = vec3(0.0f);
		float alphaFromTexture = 1.0;
		if (uUseDiffTex)
		{
			vec4 tsample = texture2D(uDiffTex, texCoord);
			alphaFromTexture = tsample.a;
			diffTex = vec3(tsample * uMaterial.Diffuse);
		}
		
		if ( uUseDiffTex )
			if ( uZebraType != 0 )
			{
				vec3 zebraSecondColor = uMaterial.Diffuse.rgb;
				if (uZebraType == 2)
					zebraSecondColor = uZebraSecondColor;
					
				vec3 texClr = vec3(texture2D(uDiffTex, texCoord));
				vec3 invTexClr = vec3(1.0 - texClr.r, 1.0 - texClr.g, 1.0 - texClr.b);
				diffTex = texClr * uZebraFirstColor + invTexClr * zebraSecondColor;
			}		
		
		vec4 diffuseLight = vec4(0.0, 0.0, 0.0, uMaterial.Diffuse.a);
		vec3 specularLight = vec3(0.0);
		
		for (int i = 0; i < uNumLight; ++i)
		{
			Light _li;
			switch (i)
			{
				case 0: _li = uLight_1; break;
				case 1: _li = uLight_2; break;
				case 2: _li = uLight_3; break;
				case 3: _li = uLight_4; break;
				case 4: _li = uLight_5; break;
				case 5: _li = uLight_6; break;
				case 6: _li = uLight_7; break;
				case 7: _li = uLight_8; break;
			}
		
			vec3 lightDir;
			if (dot(_li.Direction, _li.Direction) == 0.0)
				lightDir = normalize(_li.Position - vWorldPosition);
			else
				lightDir = -normalize(_li.Direction);
		
			float NDotL = max(dot(normal, lightDir), 0.0);
				
			vec3 diff = uMaterial.Diffuse.rgb;
			
			if (uUseDiffTex)
				diff = diffTex;
			
			diff = NDotL * diff * _li.Diffuse.rgb;
		
			vec3 lightReflect = reflect(lightDir, normal);
			float RDotV = max(dot(lightReflect, normalize(vWorldPosition-uEyePosition)), 0.01);
			vec3 spec = vec3(0.0f);
			if (uMaterial.Shininess != 0.0f)
				spec = specularMat * _li.Specular.rgb * (pow(RDotV, uMaterial.Shininess));
				
		
			diffuseLight.rgb += diff;
			specularLight += spec;
		}
		
		vec3 envDiffuse = vec3(0.0);
		if (uMaterial.EnvCoeff > 0.0)
		{
			vec3 viewDir = normalize(uEyePosition - vWorldPosition);
			//float _fresnel = fresnelTerm(viewDir, normal, 0.2);
		
			vec3 reflection = reflect(-viewDir, normal); 
		
			vec2 envTexCoord = GetSphereTexCoord(normalize(reflection));
			envDiffuse = textureLod(uEnvTex, envTexCoord, uLodEnv).rgb;
			//envDiffuse *= uMaterial.EnvCoeff;
		}
		
		vec3 fColor = mix(uMaterial.Ambient + diffuseLight.rgb, clamp(envDiffuse, 0.0, 1.0) * specularMat, uMaterial.EnvCoeff) + specularLight;
		fColor += uMaterial.Emissive;
		vec4 outColor = vec4(fColor, diffuseLight.a);
		if (uUseDiffTex)
			outColor.a *= alphaFromTexture;
		
		//vec3 gamma = vec3(1.0/2.2);
		//outColor = vec4(pow(outColor.rgb, gamma), diffuseLight.a);
		
		#ifdef THREAD
			outColor += vec4((vec3(0.0) - outColor.rgb) * threadTerm, 0.0);
		#endif
		
		//
		if (uMarkColor.w != 0.0f)
		{
			float a = uMarkColor.a;
			outColor = vec4(uMarkColor.rgb * a + outColor.rgb * (1.0f - a), outColor.a);
		}

		if (gl_FrontFacing)
			faceIndexOut = vFaceIndex + uFaceIdOffset;
		else
			faceIndexOut = -(vFaceIndex + uFaceIdOffset);
		fragColor = outColor;
		externalDepth = gl_FragCoord.z + fwidth(gl_FragCoord.z);
#ifdef OIT
		fragCount =  vec4(1);
#endif		
	}

#endif
